package com.dick.base.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dick.base.aop.SmartQuery;
import com.dick.base.dto.OrderDetail;
import com.dick.base.exception.BaseRuntimeException;
import com.dick.base.exception.DataStateNotAllowedException;
import com.dick.base.exception.ResourceNotFoundException;
import com.dick.base.mapper.*;
import com.dick.base.model.*;
import com.dick.base.parameter.ItemModifyParameter;
import com.dick.base.parameter.ItemRemoveParameter;
import com.dick.base.parameter.OrderParameter;
import com.dick.base.push.huawei.reponse.SendResponse;
import com.dick.base.push.huawei.service.SendNotifyMessageService;
import com.dick.base.push.model.NewOrderNotify;
import com.dick.base.security.dto.RoleAddParameter;
import com.dick.base.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 订单管理
 */
@Slf4j
@Service
public class OrderService {

    @Autowired
    private BusinessOrderMapper businessOrderMapper;
    @Autowired
    private BusinessOrderInfoMapper businessOrderInfoMapper;
    @Autowired
    private BusinessProjectMapper businessProjectMapper;
    @Autowired
    private BusinessCustomerMapper businessCustomerMapper;
    @Autowired
    private BusinessOrderItemMapper businessOrderItemMapper;
    @Autowired
    private BusinessProduceItemMapper businessProduceItemMapper;
    @Autowired
    private ProduceItemMapper produceItemMapper;
    @Autowired
    private ProduceService produceService;
    @Autowired
    private ProduceShelfMapper produceShelfMapper;
    @Autowired
    private BaseDictValueMapper baseDictValueMapper;

    @Autowired
    private SendNotifyMessageService sendNotifyMessageService;

    @Autowired
    private BaseNotifyMapper baseNotifyMapper;

    @Autowired
    private BaseUserRoleMapper baseUserRoleMapper;

    /**
     * 新增订单
     * @param parameter
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(OrderParameter parameter) {
        BusinessOrder order = new BusinessOrder();
        OrderParameter.OrderInfo orderInfo = parameter.getOrderInfo();
        order.setSaleDate(DateUtil.parseDate(orderInfo.getSaleDate()));
        order.setId(IdUtil.nextOrderId(order.getSaleDate()));
        order.setCustomerId(orderInfo.getCustomerId());
        order.setProjectId(orderInfo.getProjectId());
        order.setBatchNumber(orderInfo.getBatchNumber());
        order.setMaterialPrice(orderInfo.getMaterialPrice());
        order.setSalePrice(orderInfo.getSalePrice());
        order.setUrgent(orderInfo.getUrgent());
        order.setStatus(BusinessOrder.STATUS_INIT);
        order.setPriceType(parameter.getOrderInfo().getPriceType());
        order.setMemo(parameter.getOrderInfo().getMemo());
        order.setSaleName(parameter.getOrderInfo().getSaleName());
        order.setCreateTime(DateUtil.now());
        Integer quantity = 0;
        BigDecimal weight = BigDecimal.ZERO;
        List<BusinessOrderItem> itemList = new LinkedList<>();
        for (int i = 0; i < parameter.getItems().size(); i ++) {
            BusinessOrderItem item = buildOrderItem(parameter.getItems().get(i));
            item.setItemIndex(i);
            item.setOrderId(order.getId());
            item.setStatus(BusinessOrderItem.STATUS_INIT);
            item.setCreateTime(DateUtil.now());
            itemList.add(item);
            quantity += item.getQuantity();
            weight = weight.add(item.getWeight());
        }
        itemList.stream().forEach(businessOrderItemMapper::insert);
        order.setQuantity(quantity);
        order.setWeight(weight);
        order.setTotalPrice(weight.multiply(order.getSalePrice()));
        businessOrderMapper.insert(order);
        produceService.insertProduceItem(itemList, order);
    }

    /**
     * 订单分页查询
     *
     * @param order
     * @param pageInfo
     * @return
     */
    @SmartQuery(clazz = BusinessOrderInfo.class)
    public Page<BusinessOrderInfo> orderPage(BusinessOrderInfo order, PageInfo pageInfo) {
        QueryWrapper<BusinessOrderInfo> queryWrapper = SmartQueryUtil.getQueryWrapper();
        queryWrapper.ge("status", ProduceItem.STATUS_INIT);
        return businessOrderInfoMapper.selectPage(SmartQueryUtil.getPage(), queryWrapper);
    }

    /**
     * 订单详情
     * @param id
     * @return
     */
    public OrderDetail orderDetail(Long id) {
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderInfo(businessOrderMapper.selectById(id));
        orderDetail.setProject(businessProjectMapper.selectById(orderDetail.getOrderInfo().getProjectId()));
        orderDetail.setCustomer(businessCustomerMapper.selectById(orderDetail.getOrderInfo().getCustomerId()));
        orderDetail.getItems().addAll(businessOrderItemMapper.selectList(
                new QueryWrapper<BusinessOrderItem>().eq("order_id", id).orderByAsc("item_index")));
        return orderDetail;
    }

    /**
     * 发布订单
     * @param id
     */
    @Transactional
    public void publishOrder(Long id) {
        BusinessOrder order = businessOrderMapper.selectById(id);
        if (order == null) {
            throw new ResourceNotFoundException();
        }
        if (!ProduceItem.STATUS_INIT.equals(order.getStatus())) {
            throw new DataStateNotAllowedException();
        }
        updateOrderStatus(id, ProduceItem.STATUS_TO_BE_PRODUCED);
        sendNotifyToClient(order);
    }

    /**
     * 发送通知到客户端
     */
    private void sendNotifyToClient(BusinessOrder order){
        BusinessCustomer businessCustomer = businessCustomerMapper.selectById(order.getCustomerId());
        BusinessProject businessProject = businessProjectMapper.selectById(order.getProjectId());
        QueryWrapper<BusinessOrderItem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_id",order.getId());
        List<BusinessOrderItem> orderItemList = businessOrderItemMapper.selectList(queryWrapper);
        //统计 厚度，材质，宽度
        Set<String> materialSet = new HashSet<>();
        Set<String> thicknessSet = new HashSet<>();
        Set<String> widthSet = new HashSet<>();
        for (BusinessOrderItem businessOrderItem : orderItemList) {
            materialSet.add(businessOrderItem.getMaterial());
            thicknessSet.add(businessOrderItem.getThickness());
            widthSet.add(String.valueOf(businessOrderItem.getWidth()));
        }
        List<BaseDictValue> baseDictValueList = baseDictValueMapper.selectValuesByCode("TOPIC_BUSINESS");
        String topic = "";
        for (BaseDictValue baseDictValue : baseDictValueList) {
            if (baseDictValue.getCodeLabel().equals("NEW_ORDER")){
                topic = baseDictValue.getCodeValue();
            }
        }
        if (StringUtils.isEmpty(topic)){
            log.error("send order notify error!", "未配置新订单通知主题");
            return;
        }
        String materialString = materialSet.toString().replace("[","").replace("]","");
        String thicknessString = thicknessSet.toString().replace("[","").replace("]","");
        String widthString = widthSet.toString().replace("[","").replace("]","");

        saveNotify(order,businessCustomer,businessProject,orderItemList.size(),materialString,thicknessString,widthString);

        try {
            sendNotifyMessageService.sendNotification(topic,businessCustomer.getNickname(),businessProject.getName(),
                    order.getBatchNumber(),orderItemList.size(),materialString,thicknessString,widthString);
        }catch (Exception e){
            log.error("send order notify error!", e);
        }
    }

    /**
     * 保存通知记录
     */
    private void saveNotify(BusinessOrder order,BusinessCustomer businessCustomer,BusinessProject businessProject,
                            int orderItemCount,String material,String thickness,String width){

        NewOrderNotify newOrderNotify = new NewOrderNotify();
        newOrderNotify.setCustomerId(businessCustomer.getId());
        newOrderNotify.setCustomerNickName(businessCustomer.getNickname());
        newOrderNotify.setProjectName(businessProject.getName());
        newOrderNotify.setBatchNumber(order.getBatchNumber());
        newOrderNotify.setOrderSize(orderItemCount);
        newOrderNotify.setMaterialString(material);
        newOrderNotify.setThicknessString(thickness);
        newOrderNotify.setWidthString(width);

        List<Long> worker = baseUserRoleMapper.findUserIdByRoleId("worker");
        for (Long id : worker) {
            BaseNotify baseNotify = new BaseNotify();
            baseNotify.setId(IdUtil.nextId());
            baseNotify.setNotifySendTime(DateUtil.now());
            baseNotify.setNotifyRead(false);
            baseNotify.setNotifyType(BaseNotify.TYPE_BUSINESS_NEW_ORDER);
            baseNotify.setNotifyContent(JsonUtil.serialize(newOrderNotify));
            baseNotify.setUserId(id);
            baseNotifyMapper.insert(baseNotify);
        }
    }

    /**
     * 作废订单
     * @param id
     */
    @Transactional
    public void removeOrder(Long id) {
        BusinessOrder order = businessOrderMapper.selectById(id);
        if (order == null) {
            throw new ResourceNotFoundException();
        }
        if ( order.getStatus() >= ProduceItem.STATUS_PRODUCED ) {
            throw new DataStateNotAllowedException();
        }
        updateOrderStatus(id, ProduceItem.STATUS_REMOVED);
    }

    /**
     * 订单出库
     * @param id
     */
    @Transactional
    public void transferOrder(Long id) {
        BusinessOrder order = businessOrderMapper.selectById(id);
        if (order == null) {
            throw new ResourceNotFoundException();
        }
        if ( !ProduceItem.STATUS_CHECKED_OUT.equals(order.getStatus()) ) {
            throw new DataStateNotAllowedException();
        }
        updateOrderStatus(id, ProduceItem.STATUS_CHECKED_OUT);
    }

    private void updateOrderStatus(Long id, Integer status) {
        BusinessOrder orderUpdate = new BusinessOrder();
        orderUpdate.setId(id);
        orderUpdate.setUpdateTime(DateUtil.now());
        orderUpdate.setStatus(status);
        businessOrderMapper.updateById(orderUpdate);
        BusinessOrderItem orderItemUpdate = new BusinessOrderItem();
        orderItemUpdate.setUpdateTime(DateUtil.now());
        orderItemUpdate.setStatus(orderUpdate.getStatus());
        businessOrderItemMapper.update(orderItemUpdate, new QueryWrapper<BusinessOrderItem>().eq("order_id", id));
        ProduceItem produceItemUpdate = new ProduceItem();
        produceItemUpdate.setUpdateTime(DateUtil.now());
        produceItemUpdate.setStatus(orderUpdate.getStatus());
        produceItemMapper.update(produceItemUpdate, new QueryWrapper<ProduceItem>().eq("order_id", id));
    }

    private BusinessOrderItem buildOrderItem(OrderParameter.Item item) {
        BusinessOrderItem orderItem = new BusinessOrderItem();
        orderItem.setId(IdUtil.nextId());
        orderItem.setMaterial(item.getMaterial());
        orderItem.setThickness(item.getThickness());
        orderItem.setWidth(item.getWidth());
        orderItem.setLength(item.getLength());
        orderItem.setQuantity(item.getQuantity());
        orderItem.setWeight(item.getWeight());
        orderItem.setFilm(item.getFilm());
        orderItem.setChangeSpecification(item.getChangeSpecification());
        if (orderItem.getChangeSpecification()) {
            orderItem.setMaterialActual(item.getMaterial());
            orderItem.setThicknessActual(item.getThicknessActual());
            orderItem.setWidthActual(item.getWidthActual());
        } else {
            orderItem.setMaterialActual(orderItem.getMaterial());
            orderItem.setThicknessActual(orderItem.getThickness());
            orderItem.setWidthActual(orderItem.getWidth());
        }
        orderItem.setBoardId(item.getBoardId());
        orderItem.setMemo(item.getMemo());
        return orderItem;
    }

    /**
     * 订单项分页查询
     * @param produceItem
     * @param pageInfo
     * @return
     */
    @SmartQuery(clazz = BusinessProduceItem.class)
    public Page<BusinessProduceItem> itemPage(BusinessProduceItem produceItem, PageInfo pageInfo) {
        QueryWrapper<BusinessProduceItem> queryWrapper = SmartQueryUtil.getQueryWrapper();
        queryWrapper.ge("status", ProduceItem.STATUS_TO_BE_PRODUCED);
        return businessProduceItemMapper.selectPage(SmartQueryUtil.getPage(), queryWrapper);
    }

    /**
     * 订单导出
     * @param idList
     */
    public void exportOrder(List<Long> idList) {
        List<BusinessOrder> orders = businessOrderMapper.selectBatchIds(idList);
        if (orders.isEmpty() || orders.size() < idList.size()) {
            throw new ResourceNotFoundException();
        }
        if (orders.stream().filter(e -> e.getStatus() < ProduceItem.STATUS_PACKAGED).findAny().isPresent()) {
            throw new DataStateNotAllowedException();
        }
        BusinessOrder order = orders.get(0);
        ClassPathResource classPathResource = new ClassPathResource("transfer_model.xlsx");
        Workbook workbook = null;
        try {
            String title = "orders_" + System.currentTimeMillis();
            workbook = new XSSFWorkbook(classPathResource.getInputStream());
            BusinessCustomer customer = businessCustomerMapper.selectById(order.getCustomerId());
            Sheet sheet = workbook.getSheetAt(0);
            Row row2 = sheet.getRow(2);
            /**
             * 送货单位
             */
            Cell customerName = row2.getCell(0);
            customerName.setCellValue(customerName.getStringCellValue() + customer.getName());
            /**
             * 销售日期
             */
            Cell saleDate = row2.getCell(6);
            saleDate.setCellValue(saleDate.getStringCellValue() + DateUtil.format(order.getSaleDate(), "yyyy年MM月dd日"));
            /**
             * 地址
             */
            Row row3 = sheet.getRow(3);
            if (customer.getAddress() != null) {
                Cell address = row3.getCell(0);
                address.setCellValue(address.getStringCellValue() + customer.getAddress());
            }
            /**
             * 铝锭单价
             */
            if (order.getMaterialPrice() != null) {
                Cell materialPrice = row3.getCell(6);
                materialPrice.setCellValue(materialPrice.getStringCellValue() + order.getMaterialPrice());
            }
            List<ProduceItem> items = produceItemMapper.selectList(new QueryWrapper<ProduceItem>().in("order_id", idList));
            Set<Long> shelfIdSet = items.stream().map(ProduceItem::getShelfId).collect(Collectors.toSet());
            items = produceItemMapper.selectList(new QueryWrapper<ProduceItem>().in("shelf_id", shelfIdSet));
            Map<Long, BusinessOrder> projectIdToOrderMap = new HashMap<>();
            orders = businessOrderMapper.selectBatchIds(items.stream().map(ProduceItem::getOrderId).collect(Collectors.toSet()));
            orders.stream().forEach(o -> projectIdToOrderMap.put(o.getProjectId(), o));
            List<ProduceShelf> shelfList = produceShelfMapper.selectBatchIds(shelfIdSet);
            Map<Long, ProduceShelf> idToShelfMap = shelfList.stream().collect(Collectors.toMap(ProduceShelf::getId, e -> e));
            Map<Long, List<ProduceItem>> shelfIdToItemsMap = new HashMap<>();
            items.stream().forEach(item -> {
                List<ProduceItem> its = shelfIdToItemsMap.get(item.getShelfId());
                if (its == null) {
                    its = new LinkedList<>();
                    shelfIdToItemsMap.put(item.getShelfId(), its);
                }
                its.add(item);
            });
            AtomicInteger quantity = new AtomicInteger();
            AtomicReference<BigDecimal> totalWeight = new AtomicReference<>(BigDecimal.ZERO);
            AtomicReference<BigDecimal> totalMoney = new AtomicReference<>(BigDecimal.ZERO);
            AtomicInteger rowIndex = new AtomicInteger(5);
            shelfIdToItemsMap.forEach((shelfId, its) -> {
                its.stream().forEach(item -> {
                    Row row = sheet.getRow(rowIndex.getAndIncrement());
                    Cell materialCell = row.getCell(0, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    materialCell.setCellValue(item.getMaterial());
                    Cell projectCell = row.getCell(1, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    projectCell.setCellValue(item.getProjectName() + item.getProjectBatchNumber());
                    Cell thicknessCell = row.getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    thicknessCell.setCellValue(item.getThickness());
                    Cell widthCell = row.getCell(3, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    widthCell.setCellValue(item.getWidth());
                    Cell lengthCell = row.getCell(4, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    lengthCell.setCellValue(item.getLength());
                    Cell quantityCell = row.getCell(5, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    quantityCell.setCellValue(item.getQuantity());
                    quantity.addAndGet(item.getQuantity());
                });
                ProduceShelf shelf = idToShelfMap.get(shelfId);
                Row row = sheet.getRow(rowIndex.getAndIncrement());
                row.getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellValue(shelf.getShelfType() + shelf.getShelfWeight());
                row.getCell(3, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellValue("合计");
                double weight = shelf.getTotalWeight().doubleValue() - shelf.getShelfWeight().doubleValue();
                totalWeight.set(totalWeight.get().add(BigDecimal.valueOf(weight)));
                row.getCell(6, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellValue(weight);
                BusinessOrder order1 = projectIdToOrderMap.get(shelf.getProjectId());
                if (order1 != null && order1.getSalePrice() != null) {
                    double price = order1.getSalePrice().doubleValue();
                    row.getCell(7, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellValue(price);
                    row.getCell(8, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellValue(
                            BigDecimal.valueOf(price * weight).setScale(2, RoundingMode.HALF_UP).doubleValue());
                    totalMoney.set(totalMoney.get().add(BigDecimal.valueOf(price * weight)));
                }
            });
            rowIndex.getAndIncrement();
            rowIndex.getAndIncrement();
            Row row = sheet.getRow(rowIndex.getAndIncrement());
            row.getCell(3, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellValue("总计");
            row.getCell(5, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellValue(quantity.get());
            row.getCell(6, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK)
                    .setCellValue(totalWeight.get().setScale(2, RoundingMode.HALF_UP).doubleValue());
            row.getCell(8, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK)
                    .setCellValue(totalMoney.get().setScale(2, RoundingMode.HALF_UP).doubleValue());

            HttpServletResponse response = ServletUtil.currentResponse();
            response.setHeader("Content-disposition", "attachment; filename=" + title + ".xlsx");
            response.setContentType("application/msexcel");
            workbook.write(response.getOutputStream());
        } catch (IOException e) {
            log.error("export order error!", e);
        } finally {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e) {
                }
            }
        }
    }

    /**
     * 删除订单项，仅限未生产和未发布的
     * @param parameter
     */
    @Transactional
    public void removeItem(ItemRemoveParameter parameter) {
        List<BusinessOrderItem> orderItems = businessOrderItemMapper.selectBatchIds(parameter.getItemIdList());
        Optional<BusinessOrderItem> orderItem = orderItems.stream()
                .filter(item -> item.getStatus() >= ProduceItem.STATUS_PRODUCING).findAny();
        if (orderItem.isPresent()) {
            throw new BaseRuntimeException("存在不允许删除的订单项，请先返回待产");
        }
        businessOrderItemMapper.deleteBatchIds(parameter.getItemIdList());
        produceItemMapper.deleteBatchIds(parameter.getItemIdList());
        orderItems.stream().map(BusinessOrderItem::getOrderId).forEach(orderId -> {
            calculateOrderWeightAndPrice(orderId);
            businessOrderMapper.updateStatus(orderId);
        });
    }

    /**
     * 重新计算订单的重量、数量、总价
     * @param orderId
     */
    private void calculateOrderWeightAndPrice(Long orderId) {
        BusinessOrder order = businessOrderMapper.selectById(orderId);
        List<BusinessOrderItem> orderItems = businessOrderItemMapper
                .selectList(new QueryWrapper<BusinessOrderItem>().eq("order_id", orderId));
        BigDecimal weight = BigDecimal.ZERO;
        int quantity = 0;
        BigDecimal totalPrice = BigDecimal.ZERO;
        for (BusinessOrderItem item : orderItems) {
            weight = weight.add(item.getWeight());
            quantity += item.getQuantity();
            totalPrice = totalPrice.add(item.getWeight().multiply(order.getSalePrice()));
        }
        BusinessOrder orderUpdate = new BusinessOrder();
        orderUpdate.setId(orderId);
        orderUpdate.setWeight(weight.setScale(2, RoundingMode.HALF_UP));
        orderUpdate.setQuantity(quantity);
        orderUpdate.setTotalPrice(totalPrice.setScale(2, RoundingMode.HALF_UP));
        orderUpdate.setUpdateTime(DateUtil.now());
        businessOrderMapper.updateById(orderUpdate);
    }

    /**
     * 修改订单项
     * @param parameter
     */
    @Transactional
    public void itemModify(ItemModifyParameter parameter) {
        BusinessOrderItem item0 = businessOrderItemMapper.selectById(parameter.getId());
        if (item0.getStatus() > ProduceItem.STATUS_TO_BE_PRODUCED) {
            throw new BaseRuntimeException("只能修改未发布或者待产的订单项", HttpStatus.BAD_REQUEST);
        }
        BusinessOrderItem item = buildOrderItem(parameter);
        item.setId(parameter.getId());
        item.setCreateTime(null);
        item.setUpdateTime(DateUtil.now());
        businessOrderItemMapper.updateById(item);
        ProduceItem produceItemUpdate = new ProduceItem();
        produceItemUpdate.setId(item.getId());
        produceItemUpdate.setUpdateTime(DateUtil.now());
        produceItemUpdate.setWeight(item.getWeight());
        produceItemUpdate.setWidth(item.getWidthActual());
        produceItemUpdate.setMaterial(item.getMaterialActual());
        produceItemUpdate.setThickness(item.getThicknessActual());
        produceItemUpdate.setChangeSpecification(item.getChangeSpecification());
        produceItemUpdate.setLength(item.getLength());
        produceItemUpdate.setQuantity(item.getQuantity());
        produceItemUpdate.setFilm(item.getFilm());
        produceItemMapper.updateById(produceItemUpdate);
        calculateOrderWeightAndPrice(item0.getOrderId());
    }
}
